#include "auth.h"
#include "database.h"
#include <sstream>
#include <iomanip>
#include <string>
#include <cstring>

// 简单的哈希函数实现（生产环境建议使用bcrypt）
std::string simpleHash(const std::string& input) {
    std::string result;
    unsigned long hash = 5381;

    for (char c : input) {
        hash = ((hash << 5) + hash) + c; // hash * 33 + c
    }

    std::stringstream ss;
    ss << std::hex << hash;
    result = ss.str();

    // 填充到32位
    while (result.length() < 32) {
        result = "0" + result;
    }

    return result.substr(0, 32);
}

Auth::Auth(Database* db) : db_(db) {}

std::optional<User> Auth::authenticate(const std::string& username, const std::string& password) {
    std::string escapedUsername = db_->escapeString(username);
    std::string sql = "SELECT id, username, password, role FROM users WHERE username = '" + escapedUsername + "'";

    auto result = db_->query(sql);
    if (!result) {
        return std::nullopt;
    }

    MYSQL_ROW row = mysql_fetch_row(result.get());
    if (!row) {
        return std::nullopt;
    }

    std::string storedPassword = row[2];
    if (verifyPassword(password, storedPassword)) {
        User user;
        user.id = std::stoi(row[0]);
        user.username = row[1];
        user.role = row[3];
        return user;
    }

    return std::nullopt;
}

bool Auth::verifyPassword(const std::string& password, const std::string& storedPassword) {
    // 暂时使用明文比较，数据库中的密码是明文的
    // 生产环境建议使用哈希存储密码
    return password == storedPassword;
}

std::string Auth::hashPassword(const std::string& password) {
    // 使用简单的哈希函数（生产环境建议使用bcrypt或Argon2）
    return simpleHash(password);
}

bool Auth::userExists(const std::string& username) {
    std::string escapedUsername = db_->escapeString(username);
    std::string sql = "SELECT COUNT(*) FROM users WHERE username = '" + escapedUsername + "'";

    auto result = db_->query(sql);
    if (!result) {
        return false;
    }

    MYSQL_ROW row = mysql_fetch_row(result.get());
    if (!row) {
        return false;
    }

    return std::stoi(row[0]) > 0;
}
